package goboot

import (
	"go/ast"
	"go/parser"
	"go/token"
	"log"
	"os"
	"strings"
)

// OriStruct 原始结构体
type OriStruct struct {
	StructName      string           `json:"structName"`      //结构体名称
	PackageName     string           `json:"packageName"`     //包名称
	FullPackageName string           `json:"fullPackageName"` //包名称
	CommentStr      string           `json:"commentStr"`      //注释
	PackageImports  []string         `json:"packageImports"`  //导入的包
	FiledList       []OriStructFiled `json:"filedList"`       //字段列表
}

// OriStructFiled 原始结构体字段
type OriStructFiled struct {
	CommentStr string `json:"commentStr"` //注释
	Name       string `json:"name"`       //参数名称
	TypeName   string `json:"typeName"`   //参数类型名称
	TagStr     string `json:"tagStr"`     //标签
}

// OriStructMethod 原始结构体方法
type OriStructMethod struct {
	PackageName                 string                 `json:"packageName"`                 //包名称
	Name                        string                 `json:"name"`                        //方法名称
	CommentStr                  string                 `json:"commentStr"`                  //注释
	BelongStructName            string                 `json:"belongStructName"`            //所属结构体名称
	BelongStructFullPackageName string                 `json:"belongStructFullPackageName"` //所属结构体名称
	ParamList                   []oriStructMethodParam `json:"paramList"`                   //参数列表
	PackageImports              []string               `json:"packageImports"`              //导入的包
}

type oriStructMethodParam struct {
	Name     string `json:"name"`     //参数名称
	TypeName string `json:"typeName"` //参数类型名称
}

var fileFullPaths []string

var oriMethodList []OriStructMethod //原始的方法列表
var oriStructList []OriStruct       //原始的结构体列表

func scanOriCode(moduleName string) {

	listAllFiles(".")

	for _, filePath := range fileFullPaths {
		scanOriCodeOne(filePath, moduleName)
	}

}

func scanOriCodeOne(scanFilePath string, moduleName string) {
	//fmt.Println(scanFilePath)
	//读取文件内容
	fileContent, err := os.ReadFile(scanFilePath)
	if err != nil {
		panic(err)
	}

	fileAst, err := parser.ParseFile(token.NewFileSet(), scanFilePath, nil, parser.ParseComments)
	if err != nil {
		panic(err)
	}

	fullPackageName := ""

	packageName := fileAst.Name.Name
	if packageName != "main" {

		fullPackageName = moduleName + "/" + scanFilePath[2:strings.LastIndex(scanFilePath, "/")]

	}
	packageImports := []string{}

	//获得导包信息
	for _, importItem := range fileAst.Imports {
		packageImports = append(packageImports, importItem.Path.Value)
	}

	//fmt.Println(packageImports)

	//读取结构体信息
	for _, decl := range fileAst.Decls {

		if stc, ok := decl.(*ast.GenDecl); ok { //处理结构体

			for _, spec := range stc.Specs {
				if tp, ok := spec.(*ast.TypeSpec); ok {

					filedList := []OriStructFiled{}
					if stp, ok := tp.Type.(*ast.StructType); ok {
						for _, field := range stp.Fields.List {

							//字段名称
							fieldName := ""
							for _, name := range field.Names {
								fieldName = name.Name

							}

							//字段类型名称
							typeName := ""
							if starExpr, ok := field.Type.(*ast.StarExpr); ok {

								if ident, ok := starExpr.X.(*ast.Ident); ok { //地址
									typeName = "*" + ident.Name
								}
								if selectorExpr, ok := starExpr.X.(*ast.SelectorExpr); ok { //列表
									typeName = "*" + selectorExpr.Sel.Name

								}
							}
							if ident, ok := field.Type.(*ast.Ident); ok { //非地址
								typeName = ident.Name
							}

							tagStr := ""
							if field.Tag != nil {
								tagStr = field.Tag.Value
							}

							filedList = append(filedList, OriStructFiled{
								Name:       fieldName,
								TypeName:   typeName,
								TagStr:     tagStr,
								CommentStr: field.Comment.Text(),
							})

						}

					}
					oriStructList = append(oriStructList, OriStruct{
						StructName:      tp.Name.Name,
						PackageName:     packageName,
						FullPackageName: fullPackageName,
						CommentStr:      stc.Doc.Text(),
						//PackageImports: packageImports,
						FiledList: filedList,
					})
				}
			}

		} else { //处理函数

			funcDecl := decl.(*ast.FuncDecl)

			fullStr := string(fileContent[funcDecl.Type.Pos()-1 : funcDecl.Type.End()-1])

			fullStr = fullStr[0:strings.Index(fullStr, funcDecl.Name.Name+"(")]

			if !strings.Contains(fullStr, "(") {
				fullStr = ""
			} else {

				fullStr = fullStr[strings.Index(fullStr, "(")+1 : strings.Index(fullStr, ")")]
				fullStr = strings.Trim(fullStr, " ")

				fullStr = fullStr[strings.LastIndex(fullStr, " ")+1:]
				if fullStr[0] == '*' {
					fullStr = fullStr[1:]
				}

			}

			paramList := []oriStructMethodParam{}
			for _, param := range funcDecl.Type.Params.List { //参数列表
				for _, name := range param.Names {

					//参数类型名称
					typeName := ""
					if starExpr, ok := param.Type.(*ast.StarExpr); ok {

						//typeName = "*" + starExpr.X.(*ast.Ident).Name

						if ident, ok := starExpr.X.(*ast.Ident); ok {
							typeName = "*" + ident.Name
						}

						if selectorExpr, ok := starExpr.X.(*ast.SelectorExpr); ok {
							typeName = "*" + string(fileContent[selectorExpr.Pos()-1:selectorExpr.End()-1])
						}

					}
					if ident, ok := param.Type.(*ast.Ident); ok {
						typeName = ident.Name
					}

					if selectorExpr, ok := param.Type.(*ast.SelectorExpr); ok {
						typeName = string(fileContent[selectorExpr.Pos()-1 : selectorExpr.End()-1])
					}

					paramList = append(paramList, oriStructMethodParam{
						Name:     name.Name,
						TypeName: typeName,
					})

				}
			}

			oriMethodList = append(oriMethodList, OriStructMethod{
				PackageName:                 packageName,
				Name:                        funcDecl.Name.Name,
				BelongStructName:            fullStr,
				BelongStructFullPackageName: fullPackageName,
				ParamList:                   paramList,
				PackageImports:              packageImports,
				CommentStr:                  funcDecl.Doc.Text(),
			})

		}

	}

}

func listAllFiles(dirPath string) {

	dir, direrr := os.Open(dirPath)
	if direrr != nil {
		log.Fatalf("文件不存在: %v", direrr)
	}
	defer dir.Close()
	files, _ := dir.ReadDir(-1)
	for _, dirsOrFile := range files {
		if dirsOrFile.IsDir() {
			listAllFiles(dirPath + "/" + dirsOrFile.Name())

		} else {
			if !strings.HasSuffix(dirsOrFile.Name(), ".go") {
				continue
			}
			fileFullPaths = append(fileFullPaths, dirPath+"/"+dirsOrFile.Name())

		}
	}
}
